﻿<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">

	<head>
		<meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
		<title>Repositories - 仓库</title>
		<link type="text/css" rel="stylesheet" href="bootstrap.min.css" />
	</head>

	<body>

		<div class="document-contents">

			<p>
    &quot;<em>Mediates between the domain and data mapping layers using a collection-like interface for accessing domain objects</em>&quot; (Martin Fowler).</p>
			<p>
    Repositories, in practice, are used to perform database operations for 
	domain objects (<a href="Entities.html">Entity</a> and Value types). 
	Generally, a seperated repository is used for each Entity (or Aggregate 
	Root).</p>
			<p class="translation">
				仓库，在实践中是被用来为领域对象（<a href="Entities.html">实体</a>和值对象）执行数据库操作。
				通常，每个实体（或聚合根）都使用一个单独的仓库。
			</p>
			<h3 id="DocIRepositoryClasses">
    		Default Repositories - 默认仓库</h3>
			<p>
    In ASP.NET Boilerplate, a repository classes implement <strong>IRepository&lt;TEntity, 
			TPrimaryKey&gt;</strong> interface. ABP can automatically creates 
			default repositories for each entity type. You can directly <a href="Dependency-Injection.html">inject</a> 
				<strong>IRepository&lt;TEntity&gt;</strong> (or IRepository&lt;TEntity, TPrimaryKey&gt;). An example
				<a href="Application-Services.html">application service</a> uses a 
repository to insert an entity to database:</p>
			<p class="translation">
				在 ASP.NET Boilerplate 中，仓库是实现了 <strong>IRepository&lt;TEntity, TPrimaryKey&gt;</strong> 接口的类。
				ABP 可以为每个实体类型自动创建默认仓库。你可以直接<a href="Dependency-Injection.html">注入</a> <strong>IRepository&lt;TEntity&gt;</strong>（或 IRepository&lt;TEntity, TPrimaryKey&gt;）。
				下面的示例中<a href="Application-Services.html">应用程序服务</a>使用仓库来插入一个实体到数据库：
			</p>
			<pre lang="cs">public class PersonAppService : IPersonAppService
{
    <strong>private readonly IRepository&lt;Person&gt; _personRepository;</strong>

    public PersonAppService(<strong>IRepository&lt;Person&gt; personRepository</strong>)
    {
        _personRepository = personRepository;
    }

    public void CreatePerson(CreatePersonInput input)
    {        
        person = new Person { Name = input.Name, EmailAddress = input.EmailAddress };
        <strong>_personRepository.Insert(person);</strong>
    }
}</pre>
			<p>PersonAppService contructor-injects <strong>IRepository&lt;Person&gt;</strong> and 
uses the <strong>Insert</strong> method.</p>
			<p class="translation">
				PersonAppService 通过构造函数注入了 <strong>IRepository&lt;Person&gt;</strong>，并使用了它的 <strong>Insert</strong> 方法。
			</p>
			<h3>Custom Repositories - 自定义仓库</h3>
			<p>You only create a 
repository class for an entity when you need to create a custom repository 
method(s) for that entity.</p>
			<p class="translation">
				当你需要为实体创建一个自定义仓库方法时才为实体创建仓库类。
			</p>
			<h4>Custom Repository Interface - 自定义仓库接口</h4>
			<p>
	A repository definition for a Person entity is shown below:</p>
			<p class="translation">下面为 Person 实体定义了一个仓库：</p>
			<pre lang="cs">public interface IPersonRepository : IRepository&lt;Person&gt;
{

}</pre>
			<p>
    IPersonRepository extends <strong>IRepository&lt;TEntity&gt;</strong>. It's used 
	to define entities which has a primary key type of int (Int32). If your 
	entity's primary key is not int, you can extend <strong>IRepository&lt;TEntity, 
	TPrimaryKey&gt;</strong> interface as shown below:</p>
			<p class="translation">
				IPersonRepository 继承了 <strong>IRepository&lt;TEntity&gt;</strong>。
				它用于定义拥有 int（Int32）类型主键的实体。
				如果你的实体的主键不是 int，你可以从 <strong>IRepository&lt;TEntity, TPrimaryKey&gt;</strong> 接口继承，如下所示：
			</p>
			<pre lang="cs">public interface IPersonRepository : IRepository&lt;Person, long&gt;
{

}</pre>
			<h4>
    		Custom Repository Implementation - 自定义仓库实现</h4>

			<p>ASP.NET Boilerplate is designed to be independent from a 
			particular ORM (Object/Relational Mapping) framework or another 
			technique to access to database. Repositories are implemented in <strong>NHibernate</strong> and <strong>
EntityFramework</strong> as out-of-the-box. See documents to implement 
repositories in ASP.NET Boilerplate in these frameworks:</p>
			<p class="translation">
				ASP.NET Boilerplate 的设计是独立于特定 ORM（对象/关系映射）框架或其他技术来访问数据库。
				仓库是在 <strong>NHibernate</strong> 和 <strong>EntityFramework</strong> 中以开箱即用的方式实现的。
				在 ASP.NET Boilerplate 中使用这些框架实现仓库请参阅文档。
			</p>
			<ul>
				<li>
					<a href="NHibernate-Integration.html">NHibernate integration</a>
				</li>
				<p class="translation"><a href="NHibernate-Integration.html">NHibernate 整合</a></p>
				<li>
					<a href="EntityFramework-Integration.html">EntityFramework integration</a>
				</li>
				<p class="translation"><a href="EntityFramework-Integration.html">EntityFramework 整合</a></p>
			</ul>

			<h3>Base Repository Methods - 基本仓库方法</h3>
			<p>Every repository has some common methods coming from 
			IRepository&lt;TEntity&gt; interface. We will investigate most of them 
			here.</p>
			<p class="translation">
				每个仓库都有一些来自于 IRepository&lt;TEntity&gt; 接口的公共方法。
				我们将在这里研究他们中的大多数。
			</p>
			<h4 id="DocQuerying">
    Querying - 查询</h4>
			<h5 id="DocGettingSingleEntity">
    Getting single entity - 查询单实体。</h5>

			<pre lang="cs">TEntity Get(TPrimaryKey id);
Task&lt;TEntity&gt; GetAsync(TPrimaryKey id);
TEntity Single(Expression&lt;Func&lt;TEntity, bool&gt;&gt; predicate);
Task&lt;TEntity&gt; SingleAsync(Expression&lt;Func&lt;TEntity, bool&gt;&gt; predicate);
TEntity FirstOrDefault(TPrimaryKey id);
Task&lt;TEntity&gt; FirstOrDefaultAsync(TPrimaryKey id);
TEntity FirstOrDefault(Expression&lt;Func&lt;TEntity, bool&gt;&gt; predicate);
Task&lt;TEntity&gt; FirstOrDefaultAsync(Expression&lt;Func&lt;TEntity, bool&gt;&gt; predicate);
TEntity Load(TPrimaryKey id);</pre>

			<p>
				<strong>Get </strong>method is used to get an Entity with given primary key 
(Id). It throws exception if there is no entity in database with given Id.
				<strong>Single</strong> method is similar to Get but takes an expression rather 
than an Id. So, you can write a lambda expression to get an Entity. Example 
usages:</p>
			<p class="translation">
				<strong>Get</strong> 方法用来根据给定主键（Id）获取实体。如果给定 Id 的实体在数据库中不存在，那么抛出异常。
				<strong>Single</strong> 方法与 Get 类似，但使用表达式而不是 Id。因此，你可以书写一个 Lambda 表达式来获取实体。示例：
			</p>
			<pre>var person = _personRepository.Get(42);
var person = _personRepository.Single(p =&gt; p.Name == "John");</pre>
			<p>Notice that <strong>Single </strong>method throws 
exception if there is no entity with given conditions or there are more 
than one entity.</p>
			<p class="translation">
				注意，如果没有给定条件的实体或有多个实体，那么 <strong>Single</strong> 方法将抛出异常。
			</p>
			<p>
				<strong>FirstOrDefault </strong>is similar but returns <strong>null</strong> 
(instead of throwing exception)
if there is no entity with given Id or expression. Returns first found entity if 
there are more than one entity for given conditions.</p>
			<p class="translation">
				如果没有给定 Id <strong>或条件的实体，FirstOrDefault</strong> 也类似但返回<strong>空引用</strong>（代替抛出异常）。
				如果给定条件返回多个实体则返回第一个找到的实体。
			</p>
			<p>
				<strong>Load </strong>does not retrieves entity from database but creates a 
proxy object for lazy loading. If you only use Id property, Entity is not 
actually retrieved. It's retrieved from database only if you access to other 
properties of entity. This can be used instead of Get, for performance reasons. 
It's implemented in <strong>NHibernate</strong>. If ORM provider does not implements it, Load 
method works as identical as Get method.</p>
			<p class="translation">
				<strong>Load</strong> 不会从数据库取回实体，但它创建了一个代理对象用于懒加载。
				如果你只使用了 Id 属性，那么实体是不会实际取回的。只有你访问实体的其他属性时才会从数据库中取回它。
				出于性能的原因，你可以使用它来替代 Get。它是在 <strong>NHibernate</strong> 中实现的。
				如果 ORM 提供者没有实现它，那么 Load 方法与 Get 方法一样的方式工作。
			</p>

			<h5 id="DocGettingListOfEntities">Getting a list of entities - 获取实体的列表</h5>

			<pre>List&lt;TEntity&gt; GetAllList();
Task&lt;List&lt;TEntity&gt;&gt; GetAllListAsync();
List&lt;TEntity&gt; GetAllList(Expression&lt;Func&lt;TEntity, bool&gt;&gt; predicate);
Task&lt;List&lt;TEntity&gt;&gt; GetAllListAsync(Expression&lt;Func&lt;TEntity, bool&gt;&gt; predicate);
IQueryable&lt;TEntity&gt; GetAll();</pre>
			<p>
				<strong>GetAllList </strong>is used to retrieve all entities from database. 
	Overload of it can be used to filter entities. Examples:</p>
			<p class="translation">
				<strong>GetAllList </strong>被用来从数据库中取回所有的实例。它的重载可以被用来过滤实体。例如：
			</p>
			<pre lang="cs">var allPeople = _personRepository.GetAllList();
var somePeople = _personRepository.GetAllList(person => person.IsActive &amp;&amp; person.Age &gt; 42);</pre>
			<p>
				<strong>GetAll</strong> returns IQueryable&lt;T&gt;. So, you 
	can add Linq methods after it. Examples:</p>
			<p class="translation">
				<strong>GetAll</strong> 返回 IQueryable&lt;T&gt;。因此，你可以在其后添加 Linq 方法。例如：
			</p>
			<pre lang="cs">//Example 1
var query = from person in _personRepository.GetAll()
            where person.IsActive
            orderby person.Name
            select person;
var people = query.ToList();

//Example 2:
List&lt;Person&gt; personList2 = _personRepository.GetAll().Where(p =&gt; p.Name.Contains("H")).OrderBy(p =&gt; p.Name).Skip(40).Take(20).ToList();</pre>

			<p>With using GetAll, almost all queries can be written in Linq. Even it can be used in 
a join expression.</p>
			<p class="translation">
				使用 GetAll，几乎所有的查询都可以在 Linq 中编写。甚至可以被使用在 Join 表达式中。
			</p>
			<div class="bs-callout bs-callout-warning">
				<h4>About IQueryable&lt;T&gt; - 关于 IQueryable&lt;T&gt;</h4>
				<p>When you call GetAll() out of a repository method, there must be an open database 
	connection. This is because of deferred execution of IQueryable&lt;T&gt;. It 
	does not perform database query unless you call ToList() method or use the 
	IQueryable&lt;T&gt; in a foreach loop (or somehow access to queried items). 
	So, 
	when you call ToList() method, database connection must be alive. For a web 
				application, you don't care about that in most cases since MVC 
				controller methods are unit of work by default and database 
				connection is available for entire request. See <strong>
						<a href="Unit-Of-Work.html">UnitOfWork</a></strong> 
				documentation to understand it better.</p>
				<p class="translation">
					当你调用仓库方法 GetAll() 时，必须有一个打开的数据库连接。这是因为 IQueryable&lt;T&gt; 的延时执行。
					它不能执行数据库查询，除非你调用 ToList() 方法或在 foreach 循环中使用 IQueryable&lt;T&gt;（或以某种方式访问查询的项）。
					因此，当你调用 ToList() 方法时，数据库连接必须是有效的。对于 Web 应用程序，在大多数情况下你并不需要关心这点，因为 MVC 控制器方法默认是工作单元，数据库连接对于整个请求都是有效的。
					参阅 <a href="Unit-Of-Work.html">UnitOfWork</a> 文档以便更好的理解它。
				</p>
			</div>

			<h5 id="DocGettingCustom">Custom return value - 自定义返回值</h5>
			<p>There is also an additional method to provide power of IQueryable 
			that can be usable out of a unit of work.</p>
			<p class="translation">
				还有一个附加方法提供了在工作单元外使用 IQueryable：
			</p>

			<pre lang="cs">T Query&lt;T&gt;(Func&lt;IQueryable&lt;TEntity&gt;, T&gt; queryMethod);</pre>

			<p>Query method accepts a lambda (or method) that recieves 
IQueryable&lt;T&gt; and returns any type of object. Example:</p>
			<p class="translation">
				Query 方法接收一个 Lanmdba（或方法）取回 IQueryable&lt;T&gt;，并返回任意对象的类型。例如：
			</p>
			<pre lang="cs">var people = _personRepository.Query(q =&gt; q.Where(p =&gt; p.Name.Contains("H")).OrderBy(p =&gt; p.Name).ToList());</pre>

			<p>Since 
given lamda (or method) is executed inside the repository method, it's executed when 
database connection is available. You can return a list of entities, a single entity, 
a projection or something else that executes the query.</p>
			<p class="translation">
				由于给定的 Lambda（或方法）是在仓库方法内部执行的，所以它将在数据库连接有效时执行它。
				你可以返回实体的列表，单实例，投影或执行查询得到的其他内容。
			</p>
			<h4 id="DocInsertEntity">Insert - 插入</h4>
			<p>IRepository interface defines methods to insert an entity to database:</p>
			<p class="translation">IRepository 接口定义了一些方法用于插入实例到数据库：</p>
			<pre lang="cs">TEntity Insert(TEntity entity);
Task&lt;TEntity&gt; InsertAsync(TEntity entity);
TPrimaryKey InsertAndGetId(TEntity entity);
Task&lt;TPrimaryKey&gt; InsertAndGetIdAsync(TEntity entity);
TEntity InsertOrUpdate(TEntity entity);
Task&lt;TEntity&gt; InsertOrUpdateAsync(TEntity entity);
TPrimaryKey InsertOrUpdateAndGetId(TEntity entity);
Task&lt;TPrimaryKey&gt; InsertOrUpdateAndGetIdAsync(TEntity entity);</pre>
			<p>
				<strong>Insert</strong> method simply inserts new entity to database and 
returns the same inserted entity. <strong>InsertAndGetId</strong> method returns 
Id of new inserted entity. This is useful if Id is auto increment and you need 
Id of the new inserted entity. <strong>InsertOrUpdate</strong> inserts or updated given 
entity by checking it's Id's value. Lastly, <strong>InsertOrUpdateAndGetId</strong> 
returns Id of the entity after inserting or updating.</p>
			<p class="translation">
				<strong>Insert</strong> 方法简单的插入新实体到数据库并返回与已插入的实体。
				<strong>InsertAndGetId</strong> 方法返回新插入实体的 Id。如果 Id 是自增量的并你需要新插入实体的 Id，那么这是很有用的。
				<strong>InsertOrUpdate</strong> 通过检查实体的 Id 值来插入或更新给定的实体。
				最后，<strong>InsertOrUpdateAndGetId</strong> 在实体插入或更新后返回实体的 Id。
			</p>
			<h4 id="DocUpdateEntity">Update - 更新</h4>
			<p>IRepository defines methods to update an existing entity in the database. It 
gets the entity to be updated and returns the same entity object.</p>
			<p class="translation">
				IRepository 定义了一些方法用于更新在数据库中已存在的实体。它得到实体进行更新并返回相同的实体对象。
			</p>
			<pre>TEntity Update(TEntity entity);
Task&lt;TEntity&gt; UpdateAsync(TEntity entity);</pre>
<p>Most of times you don't need to explicitly call Update methods since unit of 
work system automatically saves all changes when unit of work completes. See
<a href="Unit-Of-Work.html">unit of work</a> documentation for more.</p>
			<p class="translation">
				大多数情况下，你不需要显式调用 Update 方法，因此工作单元系统在工作单元完成时自动保存所有的更改。
				有关更多信息，参阅<a href="Unit-Of-Work.html">工作单元</a>文档。
			</p>
			<h4 id="DocDeleteEntity">Delete - 删除</h4>
			<p>IRepository defines methods to delete an existing entity from the database</p>
			<p class="translation">IRepository 定义了一些方法用于从数据库中删除已存在的实体。</p>

			<pre>void Delete(TEntity entity);
Task DeleteAsync(TEntity entity);
void Delete(TPrimaryKey id);
Task DeleteAsync(TPrimaryKey id);
void Delete(Expression&lt;Func&lt;TEntity, bool&gt;&gt; predicate);
Task DeleteAsync(Expression&lt;Func&lt;TEntity, bool&gt;&gt; predicate);</pre>
			<p>First method accepts an existing entity, second one accepts Id of the entity 
to delete. The last one accepts a condition to delete all entities fit to given 
condition. Notice that all entities matches given predicate may be retrived 
from database and then deleted (based on repository implementation). So, use it carefully, it may cause performance 
problems if there are too many entities with given condition.</p>
			<p class="translation">
				第一个方法接收一个已存在的实体，第二个接收实体的 Id 来删除。最后一个接收一个条件来删除所有符合给定条件的实体。
				注意，与给定谓词匹配的所有实体会被从数据库取回，然后被删除（基于仓库的实体）。
				因此，请小心使用它，如果使用的条件有很多实体会导致性能问题。
			</p>

			<h4 id="DocOtherRepositoryMethods">Others - 其它</h4>
			<p>IRepository also provides methods to get count of entities in a table.</p>
			<p class="translation">IRepository 还提供了获取表中实体个数的方法。</p>
			<pre>int Count();
Task&lt;int&gt; CountAsync();
int Count(Expression&lt;Func&lt;TEntity, bool&gt;&gt; predicate);
Task&lt;int&gt; CountAsync(Expression&lt;Func&lt;TEntity, bool&gt;&gt; predicate);
long LongCount();
Task&lt;long&gt; LongCountAsync();
long LongCount(Expression&lt;Func&lt;TEntity, bool&gt;&gt; predicate);
Task&lt;long&gt; LongCountAsync(Expression&lt;Func&lt;TEntity, bool&gt;&gt; predicate);</pre>

			<h4 id="DocAboutAsyncMethods">About Async Methods - 关于异步方法</h4>

			<p>ASP.NET Boilerplate supports async programming model. So, repository methods 
has Async versions. Here, a sample 
				<a href="Application-Services.html">application service</a> method that uses async model:</p>
			<p class="translation">
				ASP.NET Boilerplate 支持异步编程模型。因此，仓库方法拥有异步版本。
				这里是一个使用异步模式的<a href="Application-Services.html">应用程序服务</a>示例：
			</p>
			<pre lang="cs">public class PersonAppService : AbpWpfDemoAppServiceBase, IPersonAppService
{
    private readonly IRepository&lt;Person&gt; _personRepository;

    public PersonAppService(IRepository&lt;Person&gt; personRepository)
    {
        _personRepository = personRepository;
    }

    public async Task&lt;GetPeopleOutput&gt; GetAllPeople()
    {
        var people = await _personRepository.GetAllListAsync();

        return new GetPeopleOutput
        {
            People = Mapper.Map&lt;List&lt;PersonDto&gt;&gt;(people)
        };
    }
}</pre>
			<p>GetAllPeople method is async and uses GetAllListAsync with await keyword.</p>
			<p class="translation">GetAllPeople 是异常的，并使用了 GetAllListAsync 及 await 关键字。</p>
			<p>Async may not be supported by all ORM frameworks. It's supported by 
EntityFramework. If not supported, Async repository methods works synchronously. 
Also, for example, InsertAsync works same as Insert in EntityFramework since EF 
does not write new entities to database until unit of work completes 
(a.k.a. DbContext.SaveChanges).</p>
			<p class="translation">
				不是所有的 ORM 框架都支持异步。EntityFramework 是支持的。如果不支持，异常仓库方法使用同步方式工作。
				另外，例如，在 EntityFramework 中 InsertAsync 与 Insert 的工作方式一致，因为 EF 在工作单元完成（也叫 DbContext.SaveChanges）前不能写入新实体到数据库。
			</p>

			<h3 id="DocManageConnection">Managing Database Connection - 管理数据库连接</h3>
			<p>A database 
connection is not opened or closed in a repository method. Connection management is made automatically 
by ASP.NET Boilerplate.</p>
			<p class="translation">
				在仓库方法中数据连接不能被打开或关闭。ASP.NET Boilerplate 自动进行连接管理。
			</p>
			<p>A database connection is <strong>opened </strong>and a <strong>transaction
				</strong>begins while entering a repository method automatically. When the 
method ends and returns, all changes are <strong>saved</strong>, transaction is <strong>commited
				</strong>and database connection is <strong>closed </strong>automatically by 
ASP.NET Boilerplate. If your repository method throws any type of Exception, the 
transaction is automatically <strong>rolled back </strong>and database 
connection is closed. This is true for all public methods of classes those 
implement IRepository interface.</p>
			<p class="translation">
				每当进入一个仓库方法将自动<strong>打开</strong>数据库连接及开始<strong>事务</strong>。
				当方法结束返回时，所有的变更被<strong>保存</strong>，事务被<strong>提交</strong>并且数据库连接被<strong>关闭</strong>，这些都是 ASP.NET Boilerplate 自动进行的。
				如果你的仓库方法抛出了任何类型的异常，事务将自动<strong>回滚</strong>并关闭数据库连接。
				实现 IRepository 接口的所有类及其公共方法都是这样的。
			</p>
			<p>If a repository method calls to another repository method (even a method of 
different repository) they share same connection and transaction. Connection is 
managed (opened/closed) by the first method that enters a repository. For more 
information on database connection management, see
				<a href="Unit-Of-Work.html">UnitOfWork</a> documentation.</p>
			<p class="translation">
				如果一个仓库方法调用了另一个仓库方法（甚至是不同仓库的方法），它们共享相同的连接和事务。
				连接是被进入仓库的第一个方法管理的（打开/关闭）。有关数据库连接管理的更多信息，参阅<a href="Unit-Of-Work.html">工作单元</a>文档。
			</p>
			<h3 id="DocRepositoryLifecycle">Lifetime of a Repository - 仓库的生命周期</h3>
			<p>All repository instances are <strong>Transient</strong>. It 
			means, they are instantiated per usage. See
				<a href="Dependency-Injection.html">Dependency Injection</a> 
documentation for more information.</p>
			<p class="translation">
				所有仓库实例都是瞬时的。这意味着每次使用都会被实例化。更多信息参阅<a href="Dependency-Injection.html">依赖注入</a>文档。
			</p>

			<h3 id="DocRepositoryBestPractices">Repository Best Practices - 仓库最佳实践</h3>
			<ul>
				<li>For an entity of T, use IRepository&lt;T&gt; wherever it's possible. Don't 
	create custom repositories unless it's really needed. Pre-defined repository 
	methods will be enough for many cases.</li>
				<p class="translation">
					对于泛型类型为 T 的实体，如果可能就使用 IRepository&lt;T&gt;。不要创建自定义仓库除非它是真实需要的。对于大多数情况，预定义仓库方法足够。
				</p>
				<li>If you are creating a custom repository (by extending 
	IRepository&lt;TEntity&gt;);
					<p class="translation">
						如果你创建一个自定义仓库（通过继承 IRepository&lt;TEntity&gt;）；
					</p>
					<ul>
						<li>Repository classes should be stateless. That means, you should not 
		define repository-level state objects and a repository method call 
		should not effect another call.</li>
						<p class="translation">
							仓库类应该是无状态的。这意味着，你不应该定义仓库级别的状态对象并且仓库方法的调用不应该影响其它的调用。
						</p>
						<li>Custom repository methods should not contain business logic or 
		application logic. It should just perform data-related or orm-specific 
		tasks.</li>
						<p class="translation">
							自定义仓库方法不应该包含业务逻辑或应用程序逻辑。它应该只执行数据相关或 ORM 特定任务。
						</p>
						<li>While repositories can use dependency injection, define less or no 
		dependency to other services.</li>
						<p class="translation">
							虽然仓库可以使用依赖注入，但定义时最好较少或不依赖其它服务。
						</p>
					</ul>
				</li>
			</ul>
		</div>
	</body>

</html>
